interface Dot {
    x: number;
    y: number;
    r: number; // radius 半径
    color: string;
}

export default class SpiderManEffectBg {
    private dots: Dot[] = [];
    private container: HTMLElement;
    private canvas: HTMLCanvasElement;
    private ctx: CanvasRenderingContext2D;
    private delayClear: boolean = true; // 是否延迟清除画布

    constructor(container: HTMLElement = document.body, count = 100, delayClear = true) {
        this.delayClear = delayClear;
        this.container = container;

        this.canvas = this.createCanvas();
        this.container.appendChild(this.canvas);
        this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;

        this.initStyle();

        this.dots = this.createDots(count);

        this.drawDots();

        this.container.addEventListener("mousemove", this.mouseMoveHandler);
        this.container.addEventListener("mouseout", () => {
            // 鼠标移出banner时清除画布，并重新绘制点
            this.updateCanvas(this.canvas, this.ctx);
        });
    }

    private getRandomInt(min: number, max: number): number {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min)) + min; //不含最大值，含最小值
    }

    private randomColor(): string {
        const colors = [
            "#FFC0CB",
            "#FFA07A",
            "#FFD700",
            "#ADFF2F",
            "#87CEFA",
            "#00BFFF",
            "#00CED1",
            "#1E90FF",
            "#B0C4DE",
            "#FF69B4"
        ];
        return colors[Math.floor(Math.random() * colors.length)];
    }
    private createCanvas() {
        const canvas = document.createElement("canvas");
        canvas.width = this.container.offsetWidth;
        canvas.height = this.container.offsetHeight;
        return canvas;
    }

    private initStyle() {
        this.container.style.position = "relative";
        this.canvas.style.position = "absolute";
        this.canvas.style.inset = "0";
    }

    createDots(count = 100) {
        const dots: Dot[] = [];
        for (let i = 0; i < count; i++) {
            dots.push({
                x: this.getRandomInt(0, this.canvas.width),
                y: this.getRandomInt(0, this.canvas.height),
                r: this.getRandomInt(1, 3),
                color: this.randomColor()
            });
        }
        return dots;
    }

    drawDots(ctx: CanvasRenderingContext2D = this.ctx) {
        ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.dots.forEach(dot => {
            ctx.beginPath();
            ctx.arc(dot.x, dot.y, dot.r, 0, Math.PI * 2, false);
            ctx.fillStyle = dot.color;
            ctx.fill();
        });
    }

    private updateCanvas(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
        // 鼠标移动后要清除画布，要不然鼠标移开后，画布上还残留着上次画的线条
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 清除画布把点也清除，重新绘制点。因为点信息是固定的，所以能重新画出上次一样的点。
        this.drawDots();
    }

    private mouseMoveHandler = (event: MouseEvent) => {
        if (this.delayClear) this.updateCanvas(this.canvas, this.ctx);

        const x = event.clientX - this.canvas.offsetLeft;
        const y = event.clientY - this.canvas.offsetTop;

        this.dots.forEach(dot => {
            // 勾股定理求距离
            const distance = Math.sqrt(Math.pow(x - dot.x, 2) + Math.pow(y - dot.y, 2));
            if (distance < 300) {
                // 绘制线条
                this.ctx.strokeStyle = dot.color;
                this.ctx.lineWidth = 1;
                this.ctx.beginPath();
                this.ctx.moveTo(dot.x, dot.y);
                this.ctx.lineTo(event.clientX, event.clientY);
                this.ctx.stroke();
                !this.delayClear &&
                    setTimeout(() => {
                        // 延迟一段时间后清除线条

                        // 使用 moveTo 和 lineTo 重新描绘线条，但这次使用 'destination-out' 模式来擦除
                        this.ctx.globalCompositeOperation = "destination-out";

                        this.ctx.moveTo(dot.x, dot.y);
                        this.ctx.lineTo(event.clientX, event.clientY);

                        this.ctx.stroke();

                        // 清除完毕后恢复默认的合成模式
                        this.ctx.globalCompositeOperation = "source-over";
                    }, 1000);
            }
        });
    };
}

const banner = document.querySelector(".banner") as HTMLElement;

new SpiderManEffectBg(banner, undefined, false);
